import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
/**This class would in charge of calculating the confidence of each large itemset with more than
 * two items.
 * The basic idea is simple: for each large itemset, we would try to find all its subset
 * and calculate the occurrence of each non-empty subset.
 * */

public class Confidence {
	
	/**@param largeItemSet, the itemset with more than two items and satisfy the min support.
	 * @param transactions, all the transaction record to calculate the occurence of each sub/itemset.
	 * @param minConf the required min confidence.
	 * */
	public static void calConf(List<Itemset> largeItemSet, List<Transaction> transactions, double minConf) throws IOException{
		
		Set<Itemset> allSet=new TreeSet<Itemset>();
		
		List<Rule> confResult=new ArrayList<Rule>();
		
		System.out.println("\n\nstart to calculate Conf");
		
		System.out.println(largeItemSet);
		
		//to avoid the potential duplication, we would first find all the potential subset.
		for(Itemset iSet:largeItemSet){
			
			Set<Itemset> set=genSubSets(iSet);
			
			allSet.addAll(set);
		}
		// for each subset, we count its occurence.
		for(Transaction ts:transactions){
			
			Iterator<Itemset> iT=allSet.iterator();
			
			while(iT.hasNext()){
				Itemset iSet=iT.next();
				if(ts.getItemSet().getItems().containsAll(iSet.getItems())){
					
				   iSet.addCount();
				}
			}	
		}
		//eliminate all the subset that do not satisfy the min confidence requirement.
		for(Itemset iSetSub:allSet){
			
			for(Itemset iSetLarge:largeItemSet){
				
				if(iSetLarge.getItems().containsAll(iSetSub.getItems())&&
				   iSetLarge.getItems().size()!=iSetSub.getItems().size()){
					
					
				//	double conf=((double) iSetSub.getCount())/((double)iSetLarge.getCount());
					double conf=((double) iSetLarge.getCount())/((double)iSetSub.getCount());
					if(conf>minConf){
					    double inconf=(double) iSetLarge.getCount()/(double) iSetSub.getCount();
						System.out.println(iSetSub.getItems()+"\t"+iSetLarge.getItems());
						System.out.println("ConCount:"+iSetLarge.getCount()+"\tsole count:"+iSetSub.getCount());
					    List<String> left=new ArrayList<String>();
					    List<String> right=new ArrayList<String>();
					    left.addAll(iSetSub.getItems());
					    right.addAll(iSetLarge.getItems());
					    right.removeAll(left);
						Rule r=new Rule(left, right,inconf,iSetLarge.getSup());
						confResult.add(r);
					}
				}
				
			}
		}
		// output the result.s
		File output=new File("output.txt");
		FileWriter out=new FileWriter(output, true);
		
	    Collections.sort(confResult);
	    
	    Iterator<Rule> it=confResult.iterator();
	    out.write("\n\n\n=================High-onfidence association rules (min_conf="+minConf*100+"%)\n\n");
	    while(it.hasNext()){
	    	
	    	Rule r=it.next();
	    	if(r.right.size()!=1){
	    		continue;
	    	}
	    	out.write(r.toString());
	    }
	    out.close();
	    
		
		
	}
	
	
	/**Generate all the potential subset, the implementation idea is that,
	 * first we figure out hom many items are there, and use bit map to represent the item (binary representation)
	 * and then it is obviously that we can find all the set from 1 to 2^size-1.
	 * @param iSet the itemset that we want to find all its non-empty subset.
	 * */
	public static Set<Itemset> genSubSets(Itemset iSet){
		
		Set<Itemset> set=new HashSet<Itemset>();
		
		int size= (int) Math.pow(2, iSet.size());
		Object[] allString=null;
		allString=iSet.getItems().toArray();
		
		for(int i=1;i<size-1;i++){
			
			TreeSet<String> subSet=new TreeSet<String>();
			Itemset iSubSet=new Itemset();
			List<Integer> subMem=numberToSet(i);
			
			for(Integer ii:subMem){
				String tmp=(String)allString[ii];
				subSet.add(tmp);
			}
			iSubSet.addItems(subSet);
			set.add(iSubSet);
			
		}
		return set;
	}
	  /**we use bit map to represent the items, and we need to tranfer num to the items it 
	   * correpsonding to.
	   * @param num the binary representation of item set.
	   * */
	  public static List<Integer> numberToSet(int num){
		   
		   List<Integer> result=new ArrayList<Integer>(); 
		   int i=0;
		   
		  while(num>0){
			  
			  if((num&1)==1){
				  result.add(i);
			  }
			 num= num >> 1;
			  i++;
		  }
		   
		   return result;
	   }
}
